//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
//==========================================================================
// Copyright (c) 2000-2004,  Elastos, Inc.  All Rights Reserved.
//**author:hanxu
//**y/m/d:2005/5/16
//==========================================================================

/*
 * pcidevs.cpp
 *
 * Derived from NetBSD kernel with following copyright:
 *
 * Copyright (c) 1994 Charles Hannum.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Charles Hannum.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "pcivar.h"
#include "pcireg.h"         
#include "pcidevs.h"        

#include "pcidevs_data.h"       

#include <bsp.h>
#include <elatypes.h>

EXTERN_C int CDECL sprintf(char *buf, const char *fmt, ...);
EXTERN_C char * CDECL strcpy(char* pDest, const char* pSource);


// Descriptions of known PCI classes and subclasses.

// Subclasses are described in the same way as classes, but have a
// NULL subclass pointer.

struct PCI_Class {
    char *name;
	int		val;		// as wide as pci_{,sub}class_t 
	struct PCI_Class  *subclasses;
};

static struct PCI_Class pci_subclass_prehistoric[] = {
	{ "miscellaneous",	PCI_SUBCLASS_PREHISTORIC_MISC,	NULL,},
	{ "VGA",		PCI_SUBCLASS_PREHISTORIC_VGA,		NULL,},
	{ NULL, 0, NULL,}
};

static struct PCI_Class pci_subclass_mass_storage[] = {
	{ "SCSI",		PCI_SUBCLASS_MASS_STORAGE_SCSI,		NULL},
	{ "IDE",		PCI_SUBCLASS_MASS_STORAGE_IDE,		NULL,},
	{ "floppy",		PCI_SUBCLASS_MASS_STORAGE_FLOPPY,	NULL,},
	{ "IPI",		PCI_SUBCLASS_MASS_STORAGE_IPI,		NULL,},
	{ "RAID",		PCI_SUBCLASS_MASS_STORAGE_RAID,		NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_MASS_STORAGE_MISC,	NULL,},
	{ NULL, 0, NULL,},
};

static struct PCI_Class pci_subclass_network[] = {
	{ "ethernet",		PCI_SUBCLASS_NETWORK_ETHERNET,		NULL,},
	{ "token ring",		PCI_SUBCLASS_NETWORK_TOKENRING,		NULL,},
	{ "FDDI",		PCI_SUBCLASS_NETWORK_FDDI,		NULL,},
	{ "ATM",		PCI_SUBCLASS_NETWORK_ATM,		NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_NETWORK_MISC,	NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_subclass_display[] = {
	{ "VGA",		PCI_SUBCLASS_DISPLAY_VGA,		NULL,},
	{ "XGA",		PCI_SUBCLASS_DISPLAY_XGA,		NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_DISPLAY_MISC,	NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_subclass_multimedia[] = {
	{ "video",		PCI_SUBCLASS_MULTIMEDIA_VIDEO,		NULL,},
	{ "audio",		PCI_SUBCLASS_MULTIMEDIA_AUDIO,		NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_MULTIMEDIA_MISC,	NULL,},
	{ NULL, 0, NULL,},
};

static struct PCI_Class pci_subclass_memory[] = {
	{ "RAM",		PCI_SUBCLASS_MEMORY_RAM,		NULL,},
	{ "flash",		PCI_SUBCLASS_MEMORY_FLASH,		NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_MEMORY_MISC,	NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_subclass_bridge[] = {
	{ "host",		PCI_SUBCLASS_BRIDGE_HOST,		NULL,},
	{ "ISA",		PCI_SUBCLASS_BRIDGE_ISA,		NULL,},
	{ "EISA",		PCI_SUBCLASS_BRIDGE_EISA,		NULL,},
	{ "MicroChannel",	PCI_SUBCLASS_BRIDGE_MC,		NULL,},
	{ "PCI",		PCI_SUBCLASS_BRIDGE_PCI,		NULL,},
	{ "PCMCIA",		PCI_SUBCLASS_BRIDGE_PCMCIA,		NULL,},
	{ "NuBus",		PCI_SUBCLASS_BRIDGE_NUBUS,		NULL,},
	{ "CardBus",	PCI_SUBCLASS_BRIDGE_CARDBUS,    NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_BRIDGE_MISC,	NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_subclass_communication[] = {
	{ "serial",			PCI_SUBCLASS_COMMUNICATION_SERIAL,		NULL,},
	{ "parallel",		PCI_SUBCLASS_COMMUNICATION_PARALLEL,	NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_COMMUNICATION_MISC,		NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_subclass_peripheral[] = {
	{ "PIC",		PCI_SUBCLASS_PERIPHERAL_PIC,		NULL,},
	{ "DMA",		PCI_SUBCLASS_PERIPHERAL_DMA,		NULL,},
	{ "timer",		PCI_SUBCLASS_PERIPHERAL_TIMER,		NULL,},
	{ "RTC",		PCI_SUBCLASS_PERIPHERAL_RTC,		NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_PERIPHERAL_MISC,	NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_subclass_input[] = {
	{ "keyboard",		PCI_SUBCLASS_INPUT_KEYBOARD,	NULL,},
	{ "digitiser",		PCI_SUBCLASS_INPUT_DIGITISER,	NULL,},
	{ "mouse",			PCI_SUBCLASS_INPUT_MOUSE,		NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_INPUT_MISC,		NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_subclass_docking[] = {
	{ "generic",		PCI_SUBCLASS_DOCKING_GENERIC,	NULL,},
	{ "miscellaneous",	PCI_SUBCLASS_DOCKING_MISC,		NULL,},
	{ NULL, 0, NULL, },
};

struct PCI_Class pci_subclass_processor[] = {
	{ "386",		PCI_SUBCLASS_PROCESSOR_386,			NULL,},
	{ "486",		PCI_SUBCLASS_PROCESSOR_486,			NULL,},
	{ "Pentium",	PCI_SUBCLASS_PROCESSOR_PENTIUM,		NULL,},
	{ "Alpha",		PCI_SUBCLASS_PROCESSOR_ALPHA,		NULL,},
	{ "PowerPC",	PCI_SUBCLASS_PROCESSOR_POWERPC,		NULL,},
	{ "coprocessor",PCI_SUBCLASS_PROCESSOR_COPROCESSOR,	NULL,},
	{ NULL, 0, NULL, },
};

struct PCI_Class pci_subclass_serialbus[] = {
	{ "FireWire",		PCI_SUBCLASS_SERIALBUS_FIREWIRE,	NULL,},
	{ "Access",			PCI_SUBCLASS_SERIALBUS_ACCESSS,		NULL,},
	{ "SSA",			PCI_SUBCLASS_SERIALBUS_SSA,			NULL,},
	{ "USB",			PCI_SUBCLASS_SERIALBUS_USB,			NULL,},
	{ "FibreChannel",	PCI_SUBCLASS_SERIALBUS_FIBRECHANNEL,NULL,},
	{ NULL, 0, NULL, },
};

static struct PCI_Class pci_classes[] = {
	{ "prehistoric",	PCI_CLASS_PREHISTORIC,
	    pci_subclass_prehistoric,				},
	{ "mass storage",	PCI_CLASS_MASS_STORAGE,
	    pci_subclass_mass_storage,				},
	{ "network",		PCI_CLASS_NETWORK,
	    pci_subclass_network,				},
	{ "display",		PCI_CLASS_DISPLAY,
	    pci_subclass_display,				},
	{ "multimedia",		PCI_CLASS_MULTIMEDIA,
	    pci_subclass_multimedia,				},
	{ "memory",		PCI_CLASS_MEMORY,
	    pci_subclass_memory,				},
	{ "bridge",		PCI_CLASS_BRIDGE,
	    pci_subclass_bridge,				},
	{ "communication",	PCI_CLASS_COMMUNICATION,
	    pci_subclass_communication,				},
	{ "peripheral",		PCI_CLASS_PERIPHERAL,
	    pci_subclass_peripheral,				},
	{ "input",		PCI_CLASS_INPUT,
	    pci_subclass_input,					},
	{ "docking",		PCI_CLASS_DOCKING,
	    pci_subclass_docking,				},
	{ "processor",		PCI_CLASS_PROCESSOR,
	    pci_subclass_processor,				},
	{ "serialbus",		PCI_CLASS_SERIALBUS,
	    pci_subclass_serialbus,				},
	{ "undefined",		PCI_CLASS_UNDEFINED,
	    NULL,							},
	{ NULL,0,NULL, },
};

#define	PCI_KNOWNDEV_UNSUPP	0x01		// unsupported device 
#define	PCI_KNOWNDEV_NOPROD	0x02		// match on vendor only 


void
_pci_devinfo(unsigned int id_reg, unsigned int class_reg, char *cp, int *supp)
{
	unsigned short vendor;
	unsigned short product;
	unsigned char pci_class;
	unsigned char subclass;
	unsigned char pci_interface;
	unsigned char revision;
	const char *vendor_namep, *product_namep;
	struct PCI_Class *classp;
	struct PCI_Class *subclassp;
	struct pci_knowndev *kdp;

	vendor = PCI_VENDOR(id_reg);
	product = PCI_PRODUCT(id_reg);

	pci_class = PCI_CLASS(class_reg);
	subclass = PCI_SUBCLASS(class_reg);
	pci_interface = PCI_INTERFACE(class_reg);
	revision = PCI_REVISION(class_reg);

	kdp = pci_knowndevs;

    while (NULL != kdp->vendorname) {	// all have vendor name 
        if (kdp->vendor == vendor &&
                (kdp->product == product || (kdp->flags & PCI_KNOWNDEV_NOPROD) != 0))
            break;
	    kdp++;
	}

    if (NULL == kdp->vendorname) {
	    vendor_namep = (const char *)NULL;
	    product_namep = (const char *)NULL;
		if (NULL != supp)
			*supp = 0;
    }
    else {
		vendor_namep = kdp->vendorname;
		product_namep = (kdp->flags & PCI_KNOWNDEV_NOPROD) == 0 ?
		    kdp->productname : (const char *)NULL;
		if (NULL != supp)
			*supp = ((kdp->flags & PCI_KNOWNDEV_UNSUPP) == 0);
    }

	classp = pci_classes;
	while (NULL != classp->name) {
		if (pci_class == classp->val)
			break;
		classp++;
	}

	subclassp = (NULL != classp->name) ? classp->subclasses : (struct PCI_Class *)NULL;
	while (subclassp && (NULL != subclassp->name)) {
		if (subclass == subclassp->val)
			break;
		subclassp++;
	}

	if (NULL == vendor_namep)
		cp += sprintf(cp, "vendor/product: 0x%04x/0x%04x",
		    vendor, product);
	else if (NULL != product_namep)
		cp += sprintf(cp, "%s %s", vendor_namep, product_namep);
	else
		cp += sprintf(cp, "%s, product: 0x%x",
		    vendor_namep, product);
	cp += sprintf(cp, " (");
	if (NULL == classp->name)
		cp += sprintf(cp, "pci_class/subclass: 0x%02x/0x%02x",
		    pci_class, subclass);
	else {
		cp += sprintf(cp, "%s, ", classp->name);
 		if (NULL == subclassp || NULL == subclassp->name)
		    cp += sprintf(cp, "subclass: 0x%02x",
				  subclass);
		else
		    cp += sprintf(cp, "%s", subclassp->name);
	}

	strcpy (cp, ")");
}
